#include <map>
#include <string>
#include "Aurelio.h"
#include "WellSizedVector.h"
#include "PositionVector.h"
#include "Hasher.h"
#include "MapsDisk.h"
#include <iostream>
#include <fstream>

using namespace std;


std::tr1::shared_ptr<Aurelio> Aurelio::aurelioBuarque(new Aurelio());


Aurelio::Aurelio() {
	delimiters = " ";
	numCollisions = 0;
}

//para primeira passada, vai apenas contando as palavras
list<unsigned long long> * Aurelio::updateDictionarySizes (string str, uint id/*, bool debug*/)  {
	list<unsigned long long> * wordHashes = new list<unsigned long long>;
	std::map<unsigned long long, bool> noRepetitionHash;
	while (!(str == "") ) {
		unsigned long long nextWord = getNewWordHash(str);
//		cout << "nextWord: " << nextWord << endl;
		if (nextWord == 0) 
			continue;
		if (noRepetitionHash[nextWord] == false) {
			noRepetitionHash[nextWord] = true;
			wordDictionary[nextWord].incSize();
			positionsSizeMap[nextWord][id] = 1;
		}
		else {
			numCollisions++;
			positionsSizeMap[nextWord][id] += 1;
		}
		wordHashes->push_front(nextWord);
	}
//	cout << "Total de colisoes parcial: " << numCollisions << endl;
	return wordHashes;
}


void Aurelio::printDictionary() {
	map<unsigned long long, WellSizedVector>::iterator it;
	for (it = wordDictionary.begin(); it != wordDictionary.end(); it++) {
		cout << "Palavra " << /*Hasher::reverseAbcHash(*/it->first << " \t" << (it->second).getSize() << endl;
	}
}


void Aurelio::clearDictionary() {
	wordDictionary.clear();
}

unsigned long long Aurelio::getNewWordHash(string & str/*, bool debug*/) {
	uint size = str.size();
	do {
//		cout << "." << endl;
		uint end = str.find_last_of(' '/*delimiters.c_str()*/);
		if (end != string::npos) {
			if (end == size-1) { 
//				cout  << end << " " << str.size() << endl;
				str.erase(end); //apaga primeiro caractere (q provavelmente eh um espaco)
				size--;
				continue;
			}
			else {
				string word = str.substr(end + 1, size - end - 1);
//				cout  << end << " " << str.size() << endl;
				str.erase(end);//apaga primeiro caractere (q provavelmente eh um espaco)
				size = end;
				return Hasher::abcHash(word);
			}
		}
		else if (str.size() != 0) {
			unsigned long long hash = Hasher::abcHash(str);
			str.erase();
			return hash;
		}
		else
			return 0;
//		if (debug) {
//			cout << "end= " << end << endl;
//			cout << "word = " << word << endl;
//			cout << "Mudei " << str << " tamanho: " << str.size() << endl << endl;
//		}
	}while (true);
//	cout << "Peguei " << word << " tamanho: " << word.size() << endl;
//	cout << "A string restante eh " << str << endl;
//	cout << endl;
//	for (uint i = 0; i < word.size(); i++)
//			word[i] = tolower(word[i]);
//	return Hasher::abcHash(word);
}

void Aurelio::initializeVectors() {
	map<unsigned long long, WellSizedVector>::iterator it;
	cout << "Total de colisoes: " << numCollisions << endl;
	numCollisions = 0;
	cout << "Tamanho do dicionario: " << wordDictionary.size() << endl;
	for (it = wordDictionary.begin(); it != wordDictionary.end(); it++)
		(it->second).initializeVector();
}


//segunda passada, insere o id dos documentos
void Aurelio::updateDictionary (uint id, list<unsigned long long> * wordHashes)  {
	if (wordHashes != NULL) {
	//	cout << "1" << endl;
		uint myPosition = 0;
		map<unsigned long long, uint> lastPositionHash;
		map< unsigned long long, PositionVector > tempDictionary;
		
		
		list<unsigned long long>::iterator it1;
		for (it1 = wordHashes->begin(); it1 != wordHashes->end(); it1++) {
			unsigned long long nextWord = *it1;
			if (lastPositionHash[nextWord] == 0) {
				tempDictionary[nextWord] = PositionVector(id, myPosition);
				tempDictionary[nextWord].initializeVector(positionsSizeMap[nextWord][id] - 1);
			}
			else {
				unsigned short diff = myPosition - (lastPositionHash[nextWord]-1);//-1 acochambrativo
	//			wordDictionary[nextWord].add(id, diff ); 
	//			cout << "diff: " << diff << endl;
				tempDictionary[nextWord].insertPosition(diff);
			}
			lastPositionHash[nextWord] = ++myPosition;
		}
	//	cout << "2" << endl;
		delete wordHashes;
		std::map< unsigned long long, PositionVector >::iterator it;
		for (it = tempDictionary.begin(); it != tempDictionary.end(); it++) {
	//		cout << "size: " << (it->second).size() << endl;
	//		cout << "first: " << Hasher::reverseAbcHash(it->first) << " second: " << (*((it->second).begin())) << endl;
	//		cout << Hasher::reverseAbcHash(it->first) << endl;
	//		cout << "Palavra: " << it->first << " id: " << id << endl;
	//		cout << (it->second).showPositions() << endl;
			wordDictionary[it->first].add( it->second );
		}
	//	cout << "3" << endl;
	}
}

void Aurelio::printFullDictionary() {
	map<unsigned long long, WellSizedVector>::iterator it;
	cout << "sizeWD: " << wordDictionary.size() << endl; 
	for (it = wordDictionary.begin(); it != wordDictionary.end(); it++) {
		cout << "Palavra \"" << /*Hasher::reverseAbcHash(*/it->first << "\" \tQuantidade: " << (it->second).getVectorSize() << endl;
		cout << "Presente nos documentos: " << (it->second).listDocs() << endl;
		cout << endl;
	}
}

void Aurelio::printFullDictionaryInfo() {
	ofstream e2 ("estatistica2.txt");
	uint wordTotal = 0;
	uint docsPerWord = 0;
	map<unsigned long long, WellSizedVector>::iterator it;
	for (it = wordDictionary.begin(); it != wordDictionary.end(); it++) {
		wordTotal++;
		docsPerWord += (it->second).getVectorSize();
	}
	e2 << "Total de Palavras: " << wordTotal << endl;
	e2 << "Media de Documentos: " << double(docsPerWord)/wordTotal << endl;
	e2 << "Que legal!!!" << endl << endl;
}


void Aurelio::saveAndClear() {
	mapsDisk.writeMiniMap(wordDictionary);
	numCollisions = 0;
	wordDictionary.clear();
	positionsSizeMap.clear();
}

//backup

/*
unsigned long long Aurelio::getNewWordHash(string & str, bool debug) {
	do {
//		cout << "." << endl;
		uint end = str.find_first_of(delimiters.c_str());
		if (end == 0) {
			str.erase(0, 1); //apaga primeiro caractere (q provavelmente eh um espaco)
			continue;
		}
		if (end != string::npos) {
			string word = str.substr(0, end);
			str.erase(0, end+2);//apaga primeiro caractere (q provavelmente eh um espaco)
			return Hasher::abcHash(word);
		}
		else if (str.size() != 0) {
			unsigned long long hash = Hasher::abcHash(str);
			str.erase();
			return hash;
		}
		else
			return 0;
//		if (debug) {
//			cout << "end= " << end << endl;
//			cout << "word = " << word << endl;
//			cout << "Mudei " << str << " tamanho: " << str.size() << endl << endl;
//		}
	}while (true);
//	cout << "Peguei " << word << " tamanho: " << word.size() << endl;
//	cout << "A string restante eh " << str << endl;
//	cout << endl;
//	for (uint i = 0; i < word.size(); i++)
//			word[i] = tolower(word[i]);
//	return Hasher::abcHash(word);
}*/
